/*
 * 
 * 
 * @author: Oren Zamir
 */
package org.valire.usermanagement.services;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.valire.usermanagement.NodeAccessPermissionManagementFacade;
import org.valire.usermanagement.beans.NodeAccessPermission;
import org.valire.usermanagement.beans.Role;
import org.valire.usermanagement.beans.User;
import org.valire.util.persistency.GenericJpaDaoImpl;

/**
 * The Class UserDao.
 */
@SuppressWarnings("unchecked")
public class NodeAccessPermissionDao extends GenericJpaDaoImpl<NodeAccessPermission, Integer> {

	/** The Constant logger. */
	private final static Logger	logger	= LoggerFactory.getLogger(NodeAccessPermissionDao.class);

	/**
	 * Instantiates a new user dao.
	 */
	public NodeAccessPermissionDao() {
		super(NodeAccessPermissionDao.class);
	}

	/**
	 * Inits the.
	 */
	public void init() {
		cacheAllNAPs();
	}

	/** The cached naps. */
	List<NodeAccessPermission>	cachedNaps	= null;

	/**
	 * Gets the all available roles.
	 * 
	 * @return the all available roles
	 */
	public List<NodeAccessPermission> getAllNAPs() {
		return cachedNaps;
	}

	/**
	 * Cache all na ps.
	 */
	public void cacheAllNAPs() {
		cachedNaps = getJpaTemplate().find(
				"select nodeReference from org.valire.usermanagement.beans.NodeAccessPermission as nodeReference ");
	}

	/**
	 * Gets the nAPS for role.
	 * 
	 * @param role
	 *            the role
	 * @return the nAPS for role
	 */
	public Set<NodeAccessPermission> getNAPSForRole(Role role) {
		Set<NodeAccessPermission> napsForRoles = new HashSet<NodeAccessPermission>();

		Iterator<NodeAccessPermission> it = cachedNaps.iterator();
		while (it.hasNext()) {
			NodeAccessPermission nap = (NodeAccessPermission) it.next();

			if (logger.isDebugEnabled())
				logger.debug("looking for node '{}' with role '{}' : ", nap.getNodeReference(), role.getRoleName());

			Set<Role> method_roles = nap.getRoles();
			Iterator<Role> m_it = method_roles.iterator();
			while (m_it.hasNext()) {
				Role role2 = (Role) m_it.next();
				if (logger.isDebugEnabled())
					logger.debug("node '{}' has role '{}' : ", nap.getNodeReference(), role2.getRoleName());

				if (role.equals(role2)) {
					napsForRoles.add(nap);
					break;
				}
			}
		}

		return napsForRoles;
	}

	/**
	 * Gets the nAPS for roles.
	 * 
	 * @param roles
	 *            the roles
	 * @return the nAPS for roles
	 */
	public Set<NodeAccessPermission> getNAPSForRoles(Set<Role> roles) {
		Set<NodeAccessPermission> napsForRoles = new HashSet<NodeAccessPermission>();

		Iterator<Role> role_it = roles.iterator();
		while (role_it.hasNext()) {
			Role role = (Role) role_it.next();
			napsForRoles.addAll(getNAPSForRole(role));
		}

		return napsForRoles;
	}

	/**
	 * Gets the nAPS for user.
	 * 
	 * @param user
	 *            the user
	 * @return the nAPS for user
	 */
	public Set<NodeAccessPermission> getNAPSForUser(User user) {
		Set<NodeAccessPermission> napsForUsers = new HashSet<NodeAccessPermission>();

		Iterator<NodeAccessPermission> it = cachedNaps.iterator();
		while (it.hasNext()) {
			NodeAccessPermission nap = (NodeAccessPermission) it.next();
			if (logger.isDebugEnabled())
				logger.debug("looking for node '{}' with user '{}' : ", nap.getNodeReference(), user.getUserName());

			Set<User> method_users = nap.getUsers();
			Iterator<User> m_it = method_users.iterator();
			while (m_it.hasNext()) {
				User user2 = (User) m_it.next();
				if (logger.isDebugEnabled())
					logger.debug("node '{}' has user '{}' : ", nap.getNodeReference(), user2.getUserName());

				if (user.equals(user2)) {
					logger.debug("node type  : " + nap.getType());
					napsForUsers.add(nap);
					break;
				}
			}
		}

		return napsForUsers;
	}

	/**
	 * Gets the nAPS for users.
	 * 
	 * @param users
	 *            the users
	 * @return the nAPS for users
	 */
	public Set<NodeAccessPermission> getNAPSForUsers(Set<User> users) {
		Set<NodeAccessPermission> napsForUsers = new HashSet<NodeAccessPermission>();

		Iterator<User> user_it = users.iterator();
		while (user_it.hasNext()) {
			User user = (User) user_it.next();
			napsForUsers.addAll(getNAPSForUser(user));
		}

		return napsForUsers;
	}

	/**
	 * Can role access node.
	 * 
	 * @param role
	 *            the role
	 * @param method
	 *            the method
	 * @param validationInterface
	 *            the validation interface
	 * @return true, if successful
	 */
	public boolean canRoleAccessNode(Role role, String method,
			NodeAccessPermissionManagementFacade.NAPValidationInterface validationInterface) {

		Set<Role> roles = new HashSet<Role>(1);

		roles.add(role);

		if (logger.isDebugEnabled()) {
			logger.debug("checking role '{}' for access to node '{}' ", role.getRoleName(), method);
		}

		return canRolesAccessNode(roles, method, validationInterface);
	}

	/**
	 * Can roles access node.
	 * 
	 * @param roles
	 *            the roles
	 * @param method
	 *            the method
	 * @param validationInterface
	 *            the validation interface
	 * @return true, if successful
	 */
	public boolean canRolesAccessNode(Set<Role> roles, String method,
			NodeAccessPermissionManagementFacade.NAPValidationInterface validationInterface) {
		Set<NodeAccessPermission> naps = this.getNAPSForRoles(roles);

		if (naps == null)
			return false;

		Iterator<NodeAccessPermission> iterator = naps.iterator();
		while (iterator.hasNext()) {
			NodeAccessPermission methodAccessControl = (NodeAccessPermission) iterator.next();

			// debug
			if (logger.isDebugEnabled()) {
				logger.debug("comparing : '{}' , '{}'", method, methodAccessControl.getNodeReference());
			}

			if (validationInterface.check(methodAccessControl.getNodeReference(), method)) {
				if (logger.isDebugEnabled())
					logger.debug("'{}' does equal '{}'", method, methodAccessControl.getNodeReference());
				return true;
			}

			// debug
			if (logger.isDebugEnabled())
				logger.debug("'{}' does NOT equal '{}'", method, methodAccessControl.getNodeReference());

		}

		return false;
	}

	/**
	 * Can user access node.
	 * 
	 * @param user
	 *            the user
	 * @param method
	 *            the method
	 * @param validationInterface
	 *            the validation interface
	 * @return true, if successful
	 */
	public boolean canUserAccessNode(User user, String method,
			NodeAccessPermissionManagementFacade.NAPValidationInterface validationInterface) {

		Set<User> users = new HashSet<User>(1);

		users.add(user);

		if (logger.isDebugEnabled()) {
			logger.debug("checking role '{}' for access to node '{}' ", user.getUserName(), method);
		}

		return canUsersAccessNode(users, method, validationInterface);
	}

	/**
	 * Can users access node.
	 * 
	 * @param users
	 *            the users
	 * @param method
	 *            the method
	 * @param validationInterface
	 *            the validation interface
	 * @return true, if successful
	 */
	public boolean canUsersAccessNode(Set<User> users, String method,
			NodeAccessPermissionManagementFacade.NAPValidationInterface validationInterface) {
		Set<NodeAccessPermission> naps = this.getNAPSForUsers(users);

		if (naps == null)
			return false;

		Iterator<NodeAccessPermission> iterator = naps.iterator();
		while (iterator.hasNext()) {
			NodeAccessPermission methodAccessControl = (NodeAccessPermission) iterator.next();

			// debug
			if (logger.isDebugEnabled()) {
				logger.debug("comparing : '{}' , '{}'", method, methodAccessControl.getNodeReference());
			}

			if (validationInterface.check(methodAccessControl.getNodeReference(), method)) {
				if (logger.isDebugEnabled())
					logger.debug("'{}' does equal '{}'", method, methodAccessControl.getNodeReference());
				return true;
			}

			// debug
			if (logger.isDebugEnabled())
				logger.debug("'{}' does NOT equal '{}'", method, methodAccessControl.getNodeReference());

		}

		// now check the roles of the user
		for (Iterator<User> userIt = users.iterator(); userIt.hasNext();) {
			User user = (User) userIt.next();

			if (this.canRolesAccessNode(user.getRoles(), method, validationInterface))
				return true;
		}

		return false;
	}

	/**
	 * Removes the all naps for roles.
	 * 
	 * @param roles
	 *            the roles
	 */
	public void removeAllNapsForRoles(Set<Role> roles) {
		cacheAllNAPs();
		Set<NodeAccessPermission> napsForRoles = getNAPSForRoles(roles);
		if (napsForRoles != null) {
			for (NodeAccessPermission nodeAccessPermission : napsForRoles) {
				this.delete(nodeAccessPermission);
			}
		}
		cacheAllNAPs();
	}

	/**
	 * Removes the all naps for users.
	 * 
	 * @param users
	 *            the users
	 */
	public void removeAllNapsForUsers(Set<User> users) {
		cacheAllNAPs();
		Set<NodeAccessPermission> napsForUsers = getNAPSForUsers(users);
		if (napsForUsers != null) {
			for (NodeAccessPermission nodeAccessPermission : napsForUsers) {
				this.delete(nodeAccessPermission);
			}
		}
		cacheAllNAPs();
	}

}
